在學習資訊的過程中,曾今懷疑雜湊值的碰撞機率,一句機率很低,並無法說服我。接觸密碼學與機統後,才明白那個『很低』到底有多低。
也有遇到別人問過,『區塊鏈用公開金鑰加密演算法的私鑰採生地址,那如果有兩個人同時生成一樣的私鑰,或者不同公鑰透過雜湊後的到相同的值,不就能夠操作別的人錢包嗎?』,對於問題的答案是肯定的,但是這個機率,比天上星星數量的倒數遠遠還要小。
說一個密碼學機率的基礎問題,『你覺得一個30人的班級,有兩個人同一天生日的機率有多少?』,答案是70%。365天這麼多天,機率有這麼大嗎?數學論證就是如此,附上維基百科 Birthday problem,與使用這個原理產生的密碼學攻擊 Birthday attack。
透過生日問題,理解了碰中機率與數量的關係後,那試算一下橢圓曲線密碼學 SECP256k1 所能產生的私鑰個數約有 2^256 ~= 1.2 × 10^77
,然後再比較一下剛剛維基百科中的表格,要大於 50% 碰撞率至少要有 4.0 × 10^38
個。這到量到底有多大?台灣約有 2,300 萬人,日本約有1億2千人,全世界在2017年4月統計約有75億,銀河系恆星數目約有1000-4000億 (2.5×10^11)。(以上數字都只是估算,僅供參考)
這樣你感受到其中的量級差距與碰撞機率了嗎?雜湊函示 sha256 也是相同的。
先不論到底有多『理論上有可能,實際上機率趨近於零』的問題。假設今天你相信你就是這麼歐洲人,玩遊戲課金抽寶沒有抽不到的,那就讓來寫一個 lucky wallet 吧!
使用 python 實作,隨機建立一個ECC公、私鑰並生成 ETH 錢包地址,查詢是否有錢,有的話就轉到目標地址。理論上,這個程式永遠不會執行成功啦!如果真的執行成功,記得分我一點啊!
# https://github.com/SecondDim/PracticePython/blob/master/Ethereum/lucky_wallet.py
# 如果喜歡這支程式,歡迎施捨一點 Ether~
# ETH:0xe44bd95c1ddb1e2d6ebdd08d39b18264f530d5cb
# =============================================
#! /usr/bin/python3
# pip install ecdsa
# pip install pysha3
# Ether 使用 ecc 簽章
from ecdsa import SigningKey, SECP256k1
import sha3
# 與 RPC 連線
from web3 import Web3, HTTPProvider
# 編碼
import rlp
from ethereum.transactions import Transaction
import time
import binascii
lucky_wallet = ""
rpc_server = ""
def main():
# 生成私鑰、公鑰
priv = SigningKey.generate(curve=SECP256k1)
pub = priv.get_verifying_key().to_string()
# 生成錢包地址
keccak = sha3.keccak_256()
keccak.update(pub)
guess_address = "0x" + keccak.hexdigest()[24:]
# 建立 RPC 連線,並取得帳戶餘額
w3 = Web3(HTTPProvider(rpc_server))
guess_address_balance = w3.eth.getBalance(guess_address)
# 理論上永遠不會是 True
if guess_address_balance != 0:
# 計算手續費並將餘額轉入指定地址
gas_price = w3.eth.gasPrice
tx_val = guess_address_balance - (gas_price * 21000)
if tx_val <= 0:
tx_val = guess_address_balance - (1 * 21000)
tx = Transaction(
nonce=w3.eth.getTransactionCount(guess_address),
gasprice=gas_price,
startgas=21000,
to=lucky_wallet,
value=tx_val,
data=b'',
)
tx.sign(priv.to_string())
raw_tx = rlp.encode(tx)
raw_tx_hex = w3.toHex(raw_tx)
tx_hash = w3.eth.sendRawTransaction(raw_tx_hex)
print("Tx: " + tx_hash)
if __name__ == '__main__':
if lucky_wallet == "" or rpc_server == "":
print("You should setup arg lucky_wallet and rpc_server first!")
else:
print("starting......")
while True:
try:
main()
except KeyboardInterrupt:
break
except Exception as e:
print(e)
finally:
pass
time.sleep(0.01)